#!/usr/bin/env python3 """A brutalist CommonMark converter. This converter inserts some formatting marks into the HTML text. Made by dcz. License: GPLv3. """ try: import commonmark import commonmark.blocks import re commonmark.blocks.reMaybeSpecial = re.compile(r'^[\$#`~*+_=<>0-9-]') except ImportError: import CommonMark as commonmark import latex2mathml.converter """Renders formulas as MathML using latex syntax""" class FormulaMixIn: def formula(self, node, entering): self.cr() self.buf += latex2mathml.converter.convert(node.string_content, display='') self.cr() """Render CommonMark in a copy-pasteable way. Almost.""" class IdemMark(commonmark.HtmlRenderer, FormulaMixIn): def heading(self, node, entering): commonmark.HtmlRenderer.heading(self, node, entering) if entering: self.out('#' * node.level + ' ') def paragraph(self, node, entering): commonmark.HtmlRenderer.paragraph(self, node, entering) if entering and node.parent.t == 'block_quote': self.out('> ') def code(self, node, entering): self.out('`') commonmark.HtmlRenderer.code(self, node, entering) self.out('`') def emph(self, node, entering): if entering: self.out('*') commonmark.HtmlRenderer.emph(self, node, entering) if not entering: self.out('*') def strong(self, node, entering): if entering: self.out('**') commonmark.HtmlRenderer.strong(self, node, entering) if not entering: self.out('**') def code_block(self, node, entering): self.cr() self.tag('pre') self.out('```') self.cr() self.tag('code', self.attrs(node)) self.out(node.literal) self.tag('/code') self.out('```') self.tag('/pre') self.cr() class FormulaMark(commonmark.HtmlRenderer, FormulaMixIn): pass def add_dict(a, b): a = dict(a) a.update(b) return a class Formula(commonmark.blocks.Block): accepts_lines = False @staticmethod def continue_(parser=None, container=None): # A formula can never container > 1 line, so fail to match: return 1 @staticmethod def finalize(parser=None, block=None): return @staticmethod def can_contain(t): return False class FormulaParser(commonmark.Parser): blocks = add_dict(commonmark.Parser().blocks, {'formula': Formula}) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.block_starts = BlockStarts() class BlockStarts(commonmark.blocks.BlockStarts): METHODS = commonmark.blocks.BlockStarts.METHODS + ['formula'] @staticmethod def formula(parser, container=None): match = '$$$ ' if not parser.indented and parser.current_line.startswith(match): parser.advance_offset(len(match), False) parser.close_unmatched_blocks() container = parser.add_child('formula', parser.next_nonspace) container.string_content = parser.current_line[parser.offset:] parser.advance_offset(len(parser.current_line) - parser.offset, False) return 2 return 0 def convert(md): ast = FormulaParser().parse(md) return IdemMark().render(ast) def convert_formula(md): ast = FormulaParser().parse(md) return FormulaMark().render(ast) if __name__ == '__main__': import sys if len(sys.argv) > 1: print('Usage: {} < file.md'.format(sys.argv[0])) sys.exit(1) sys.stdout.write(convert(sys.stdin.read()))